package com.lezhi.socialgram.util;

import static org.slf4j.LoggerFactory.getLogger;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;

import com.google.common.collect.Lists;

import eu.infomas.annotation.AnnotationDetector;
import eu.infomas.annotation.AnnotationDetector.Reporter;
import eu.infomas.annotation.AnnotationDetector.TypeReporter;


public class AnnotationScanUtils {

    private static final Logger log = getLogger(AnnotationScanUtils.class);

    private AnnotationScanUtils() {
    }

    private static List<Class<?>> supportAnnotationClasses = new ArrayList<>();//特定注解作用下的类类型列表

    private static Map<Class<?>, Class<? extends Annotation>> classesMap = null;//类类型-注解类型映射


    /**
     * 初始化列表和Map
     *
     * @param annotaionType            用户自定义注解类型.
     * @param supportAnnotationPackage 注解作用下的类所在包
     */
    private static void init(Class<? extends Annotation> annotaionType, String supportAnnotationPackage) {

        if (StringUtils.isNotBlank(supportAnnotationPackage)) {

            String[] sitenameClazzArray = StringUtils.split(supportAnnotationPackage, ";");

            classesMap = scan(annotaionType, sitenameClazzArray);

            for (Entry<Class<?>, Class<? extends Annotation>> entry : classesMap.entrySet()) {
                supportAnnotationClasses.add(entry.getKey());
            }
        }
    }


    /**
     * 得到特定包下的特定注解作用的Class
     *
     * @param annotaionType            要求扫描的注解类型
     * @param supportAnnotationPackage 扫描的包名称，如果有多个，则以';'分隔
     * @return 被特定注解作用且位于制定包下的所有具体类类型列表
     */
    public static List<Class<?>> getSupportClasses(Class<? extends Annotation> annotaionType, String supportAnnotationPackage) {

        init(annotaionType, supportAnnotationPackage);

        return supportAnnotationClasses;
    }


    /**
     * 得到类类型-注解类型映射
     *
     * @return
     */
    public static Map<Class<?>, Class<? extends Annotation>> getObjectAnnotationMap(Class<? extends Annotation> annotaionType, String supportAnnotationPackage) {

        init(annotaionType, supportAnnotationPackage);

        return classesMap;
    }


    /**
     * 扫描类注解.
     *
     * @param annotationClass
     * @param packages
     * @return
     */
    private static Map<Class<?>, Class<? extends Annotation>> scan(
            final Class<? extends Annotation> annotationClass,
            String... packages) {
        final Map<Class<?>, Class<? extends Annotation>> classesMap = new HashMap<>();
        final Reporter reporter = new TypeReporter() {

            @SuppressWarnings("unchecked")
            @Override
            public Class<? extends Annotation>[] annotations() {
                return new Class[]{annotationClass};
            }

            @Override
            public void reportTypeAnnotation(
                    Class<? extends Annotation> annotation, String className) {
                try {
                    classesMap.put(Class.forName(className), annotation);
                } catch (ClassNotFoundException e) {
                    log.error("Class not found: {}", className, e);
                }
            }

        };
        startScan(reporter, packages);
        return classesMap;
    }


    private static void startScan(
            final AnnotationDetector.Reporter reporter, String... packageNames) {
        final AnnotationDetector cf = new AnnotationDetector(reporter);
        try {
            // 解决在web容器下扫描不到类的问题.
            URL url = Thread.currentThread().getContextClassLoader()
                    .getResource("");
            File file = new File(url.getPath());
            System.out.println("file" + file);
            File[] files = file.listFiles(
                    (File pathname) -> pathname.isDirectory() && !pathname.isHidden()
            );
            System.out.println("files" + files);
            System.out.println("files.length=" + file.length());
            List<String> fileNames = Lists.newLinkedList();
            for (File one : files) {
                fileNames.add(one.getName());
            }
            log.debug("san path:{}", fileNames);
            cf.detect(fileNames.toArray(new String[fileNames.size()]));
            // 这里扫描全部可能会有性能问题
            // XXX 在java项目中可以扫描到jar文件中的类，在web项目中不行.
            cf.detect();

        } catch (IOException e) {
            log.error("scan package error packages:{}",
                    Arrays.toString(packageNames), e);
        }
    }


    /**
     * test
     *
     * @param args
     */
    public static void main(String[] args) {
        File file = new File("/Users/dz/oschina/socialgram/target/socialgram-1.0.jar!/BOOT-INF/classes!");
        File[] files = file.listFiles(
                (File pathname) -> pathname.isDirectory() && !pathname.isHidden()
        );
        System.out.println(files);
    }

}